<?php

namespace app\library;

/**
 * CSV 导出工具
 * Class Csv
 * @package app\library
 */
class Csv
{
    /**
     * 写入CSV文件头部
     * @param string $filename 导出文件
     * @param array $headers CSV头部(一维数组)
     */
    public static function header($filename, array $headers)
    {
        header('Content-Type: application/octet-stream');
        header("Content-Disposition: attachment; filename=" . iconv('UTF-8', 'GB2312//IGNORE', $filename));
        $handle = fopen('php://output', 'w');
        foreach ($headers as $key => $value) $headers[$key] = iconv("UTF-8", "GB2312//IGNORE", $value);
        fputcsv($handle, $headers);
        if (is_resource($handle)) fclose($handle);
    }

    /**
     * 写入CSV文件内容
     * @param array $list 数据列表(二维数组或多维数组)
     * @param array $rules 数据规则(一维数组)
     */
    public static function body(array $list, array $rules)
    {
        $handle = fopen('php://output', 'w');
        foreach ($list as $data) {
            $rows = [];
            foreach ($rules as $rule) $rows[] = self::parseKeyDotValue($data, $rule);
            fputcsv($handle, $rows);
        }
        ob_flush();
        flush();
        if (is_resource($handle)) fclose($handle);
        exit;
    }

    /**
     * 根据数组key查询(可带点规则)
     * @param array $data 数据
     * @param string $rule 规则，如: order.order_no
     * @return mixed
     */
    public static function parseKeyDotValue(array $data, $rule)
    {
        list($temp, $attr) = [$data, explode('.', trim($rule, '.'))];
        while ($key = array_shift($attr)) $temp = isset($temp[$key]) ? $temp[$key] : $temp;
        return (is_string($temp) || is_numeric($temp)) ? @iconv('UTF-8', 'GB2312//IGNORE', "{$temp}") : '';
    }
    
    /**
     * 导出CSV文件
     * @param string $filename 导出文件
     * @param array $header 头部(一维数组) 如['time|getTime,Y-m-d H:i:s' => '时间']
     * @param array $data 数据(二维数组)
     */
    public static function export($filename, $header, $data) 
    {
        header('Content-Encoding: UTF-8');
        header("Content-type:application/vnd.ms-excel;charset=UTF-8");
        header('Content-Disposition: attachment;filename="' . $filename . '.csv"');
        $handle = fopen('php://output', 'a');
        fwrite($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
        // 写入CSV文件头部
        fputcsv($handle, array_values($header));
        // 写入CSV文件内容
        self::parseData($handle, $header, $data);
        ob_flush();
        flush();
        if (is_resource($handle)) fclose($handle);
        exit;
    }
    
    /**
     * 分步导出CSV文件
     * @param string $filename 导出文件
     * @param array $header 头部(一维数组) 如['time|getTime,Y-m-d H:i:s' => '时间']
     * @param query $query 查询构造器
     */
    public static function exportByStep($filename, $header, $query)
    {
        header('Content-Encoding: UTF-8');
        header("Content-type:application/vnd.ms-excel;charset=UTF-8");
        header('Content-Disposition: attachment;filename="' . $filename . '.csv"');
        $handle = fopen('php://output', 'a');
        fwrite($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
        // 写入CSV文件头部
        fputcsv($handle, array_values($header));
        // 写入CSV文件内容
        $nums = 10000;
        $step = ceil($query->count() / $nums);
        for ($i = 1; $i <= $step; $i++) {
            $data = $query->page($i, $nums)->select();
            self::parseData($handle, $header, $data);
            ob_flush();
            flush();
        }
        if (is_resource($handle)) fclose($handle);
        exit;
    }
    
    /**
     * 根据数组key解析数据
     * @param resource $handle 输出流
     * @param array $header 头部
     * @param array $data 数据
     */
    public static function parseData($handle, $header, $data)
    {
        $header_key = array_keys($header);
        foreach ($data as $key => $value) {
            $row = [];
            foreach ($header_key as $k => $v) {
                if (strpos($v, '|') !== false) {
                    list($field, $method) = explode('|', $v);
                    if (isset($value[$field])) {
                        if (strpos($method, ',') !== false) {
                            list($method, $param) = explode(',', $method);
                            $cell = $method($value[$field], $param);
                        } else {
                            $cell = $method($value[$field]);
                        }
                        $row[$field] = $cell;
                    }
                } else {
                    isset($value[$v]) && $row[$v] = $value[$v];
                }
            }
            fputcsv($handle, $row);
        }
    }

}
